#ifndef RUBIK_H_
#define RUBIK_H_

#include "main.h"
#include "cube.h"
#include "colorpad.h"

#include <vector>

using namespace std;

struct rubik_nine {
	cube *f[9];
	struct rubik_nine *nines[8];
	
	void addCube (int ind, cube *c) {
		f[ind]=c;
	} 
};

struct numset12 {

	int z, y, x, w, v, u, t, s, r, q, p, o;

	void setNums (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l) {
		z=a;y=b;x=c;w=d;v=e;u=f;t=g;s=h;r=i;q=j;p=k; o=l;
	}

	int getNums(int i) {
		switch (i) {
		case 0:
			return z;
			break;
		case 1:
			return y;
			break;
		case 2:
			return x;
			break;
		case 3:
			return w;
			break;
		case 4:
			return v;
			break;
		case 5:
			return u;
			break;
		case 6:
			return t;
			break;
		case 7:
			return s;
			break;
		case 8:
			return r;
			break;
		case 9:
			return q;
			break;
		case 10:
			return p;
			break;
		case 11:
			return o;
			break;
		}
	}
};

enum rotate_d {
	STAY, LEFT_D, RIGHT_D, UP_D, DOWN_D, CW_D, CCW_D
};

enum ROT_REGION {
	rS, rM
};

struct RUBIK_ORIENTATION {
	RUBIK_SIDES frontSide;
	RUBIK_SIDES topSide;

	void setOrientation (RUBIK_SIDES front, RUBIK_SIDES top) {
		frontSide=front;
		topSide=top;
	}
};

enum RUBIK_COLORS {
	R_GRAY, R_BLACK, R_RED, R_ORANGE, R_YELLOW, R_GREEN, R_BLUE, R_VIOLET
};

class rubik {

public:
	RUBIK_SIDES myLeft;
	RUBIK_SIDES myRight;
	RUBIK_SIDES myTop;
	RUBIK_SIDES myBottom;

	rubik();
	RUBIK_SIDES getSide(int indA);

	void reinitCubes();
	void render();
	void rotate(RUBIK_SIDES s, RUBIK_ROTATE r);	
	void reset();
	void renderflat();

	void rubikRotateX(RUBIK_ROTATE r);
	void rubikRotateY(RUBIK_ROTATE r);
	void rubikRotateZ(RUBIK_ROTATE r);

	void rotateTY (RUBIK_ROTATE r);
	void rotateMY (RUBIK_ROTATE r);
	void rotateLY (RUBIK_ROTATE r);

	void rotateRZ (RUBIK_ROTATE r);
	void rotateMZ (RUBIK_ROTATE r);
	void rotateLZ (RUBIK_ROTATE r);
	
	void rotateRX (RUBIK_ROTATE r);
	void rotateMX (RUBIK_ROTATE r);
	void rotateLX (RUBIK_ROTATE r);

	void nextOrient (rotate_d d);

	void rotateMyFaces(RUBIK_SIDES s, RUBIK_ROTATE r);
	void rotateFace(int indA, int indB, RUBIK_ROTATE r);

	void rotateSides(numset12 a, RUBIK_ROTATE r);
	void rotateSides(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, RUBIK_ROTATE o);
	void rotateSides(rotate_d d);	
	

	void test();
	RUBIK_ORIENTATION getOrientation(RUBIK_ORIENTATION o);
	
	void readSolution(string filename);
	void writeConfiguration(string filename);
	void printSolutions();
	string nextSolution();
	
	inline RUBIK_ORIENTATION &getOrientation() {RUBIK_ORIENTATION &r=orient; return r;}
	
	inline void setX(int x) {deg_x=((deg_x+x) % 360);}
	inline void setY(int y) {deg_y=(deg_y+y)%360;}
	inline void setZ(int z) {deg_z=(deg_z+z)%360;}
	inline void resetX() {deg_x=0;}
	inline void resetY() {deg_y=0;}
	inline void resetZ() {deg_z=0;}
	inline void resetAll() {for (int i=0;i<27;i++) {cubes[i]->resetAll();}} 
	inline void resetMyAll() {resetX(); resetY(); resetZ();}
	inline void checkXYZ () {cout << "X: " << deg_x << "Y: " << deg_y << "Z: " << deg_z <<endl;}
	inline void testId() {for (int i=0;i<54;i++) {cout<< faces[i]->id << " ";} cout << "" << endl;}

	void makeRotation();
	cube *cubes[27]; 
	void resetFaceColor(float x, float y, vec3 *c);

private:
	
	C_Face *faces[54];
	vec2 positions[54];
	vector<string> solutions;
	int facet_w;
	int facet_h;
	int solutioncount;

	RUBIK_ORIENTATION orient;
	rubik_nine *nines[8]; 

	int cube_size;
	int deg_z;
	int deg_y;
	int deg_x;
};
#endif